package cn.csc.module.shoppingCard.service.project;

import cn.csc.framework.common.pojo.PageResult;
import cn.csc.framework.common.util.collection.CollectionUtils;
import cn.csc.module.shoppingCard.controller.admin.project.vo.*;
import cn.csc.module.shoppingCard.convert.project.CardProjectConvert;
import cn.csc.module.shoppingCard.dal.dataobject.card.CardDO;
import cn.csc.module.shoppingCard.dal.dataobject.project.CardProjectDO;
import cn.csc.module.shoppingCard.dal.mysql.card.CardMapper;
import cn.csc.module.shoppingCard.dal.mysql.project.CardProjectMapper;
import cn.csc.module.shoppingCard.service.card.CardService;
import cn.csc.module.shoppingCard.util.CardNumberUtil;
import cn.csc.module.system.api.user.AdminUserApi;
import cn.csc.module.system.api.user.dto.AdminUserRespDTO;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static cn.csc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csc.module.shoppingCard.enums.ErrorCodeConstants.*;

/**
 * 购物卡项目 Service 实现类
 *
 * @author liuchuang
 */
@Service
@Validated
@Slf4j
public class CardProjectServiceImpl extends ServiceImpl<CardProjectMapper, CardProjectDO> implements CardProjectService {

    @Autowired
    private CardService cardService;

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    private AdminUserApi adminUserApi;

    @Value("${csc.workIp}")
    private String workIp;


    @Transactional
    @Override
    public BigInteger createCardProject(CardProjectCreateReqVO createReqVO) {
        // 插入
        CardProjectDO cardProject = CardProjectConvert.INSTANCE.convert(createReqVO);
        baseMapper.insert(cardProject);
        // 前包含后不包含
        List<CardDO> collect = IntStream.range(0, cardProject.getTotalNum()).mapToObj(i -> {
            CardDO cardDO = new CardDO();
            cardDO.setProjectId(cardProject.getId());
            cardDO.setProjectName(cardProject.getName());
            cardDO.setCardNumber(CardNumberUtil.getCardNumber(workIp));
            cardDO.setFaceValue(cardProject.getFaceValue());
            cardDO.setBalance(cardProject.getFaceValue());
            cardDO.setRestriction(cardProject.getRestriction());
            cardDO.setComment(cardProject.getComment());
            cardDO.setExpireTime(cardProject.getExpireDate());
            cardDO.setPassword(IdUtil.fastSimpleUUID().substring(0,10));
            return cardDO;
        }).collect(Collectors.toList());
        cardService.saveBatch(collect,1500);

        // 返回
        return cardProject.getId();
    }

    @Transactional
    @Override
    public void updateCardProject(CardProjectUpdateReqVO updateReqVO) {
        // 校验存在
        CardProjectDO cardProjectDO = validateCardProjectExists(updateReqVO.getId());
        // 校验有效期
        if (updateReqVO.getExpireDate().isBefore(cardProjectDO.getExpireDate())) {
            throw exception(CARD_PROJECT_EXPIRE_DATE_NOT_ALLOW);
        }
        if (updateReqVO.getTotalNum() > cardProjectDO.getTotalNum()) {
            List<CardDO> collect = IntStream.range(0, updateReqVO.getTotalNum() - cardProjectDO.getTotalNum()).mapToObj(i -> {
                CardDO cardDO = new CardDO();
                cardDO.setProjectId(cardProjectDO.getId());
                cardDO.setProjectName(updateReqVO.getName());
                cardDO.setCardNumber(CardNumberUtil.getCardNumber(workIp));
                cardDO.setFaceValue(updateReqVO.getFaceValue());
                cardDO.setBalance(updateReqVO.getFaceValue());
                cardDO.setRestriction(updateReqVO.getRestriction());
                cardDO.setComment(updateReqVO.getComment());
                cardDO.setExpireTime(updateReqVO.getExpireDate());
                cardDO.setPassword(IdUtil.fastSimpleUUID().substring(0, 10));
                return cardDO;
            }).collect(Collectors.toList());
            cardService.saveBatch(collect, 1500);
        } else if (updateReqVO.getTotalNum() < cardProjectDO.getTotalNum()) {
            throw exception(CARD_PROJECT_TOTAL_NUM_NOT_ALLOW);
        }
        cardService.update(new LambdaUpdateWrapper<CardDO>().set(CardDO::getProjectName, updateReqVO.getName())
                .set(CardDO::getExpireTime, updateReqVO.getExpireDate())
                .set(CardDO::getRestriction, updateReqVO.getRestriction())
                .set(CardDO::getComment, updateReqVO.getComment())
                .eq(CardDO::getProjectId, cardProjectDO.getId()));

        // 更新
        CardProjectDO updateObj = CardProjectConvert.INSTANCE.convert(updateReqVO);
        baseMapper.updateById(updateObj);
    }

    @Override
    public void deleteCardProject(BigInteger id) {
        // 校验存在
        validateCardProjectExists(id);
        // 删除
        baseMapper.deleteById(id);
    }

    private CardProjectDO validateCardProjectExists(BigInteger id) {
        CardProjectDO exist = baseMapper.selectById(id);
        if (exist == null) {
            throw exception(CARD_PROJECT_NOT_EXISTS);
        }
        return exist;
    }

    @Override
    public CardProjectDO getCardProject(BigInteger id) {
        return baseMapper.selectById(id);
    }

    @Override
    public List<CardProjectDO> getCardProjectList(Collection<BigInteger> ids) {
        return baseMapper.selectBatchIds(ids);
    }

    @Override
    public List<CardProjectDO> ListAll() {
        return baseMapper.selectList();
    }

    @Override
    public PageResult<CardProjectDO> getCardProjectPage(CardProjectPageReqVO pageReqVO) {
        PageResult<CardProjectDO> pageResult = baseMapper.selectPage(pageReqVO);
        log.info("getCardProjectPage : {}", pageResult);
        if (CollectionUtils.isAnyEmpty(pageResult.getList())){
            return pageResult;
        }
        List<Long> updaters = pageResult.getList().stream().map(CardProjectDO::getUpdater).filter(StringUtils::hasLength).distinct()
                .map(Long::valueOf).collect(Collectors.toList());
        // 购物卡项目的用户数据来自system模块
        List<AdminUserRespDTO> users = adminUserApi.getUserList(updaters).getCheckedData();
        log.info("getCardProjectPage users : {}", users);
        if (!CollectionUtils.isAnyEmpty(users)){
            Map<Long, String> userMap = CollectionUtils.convertMap(users, AdminUserRespDTO::getId, AdminUserRespDTO::getUsername);
            pageResult.getList().stream().filter(c -> StringUtils.hasLength(c.getUpdater()))
                    .forEach(c -> c.setUpdater(userMap.get(Long.valueOf(c.getUpdater()))) );
        }

        return pageResult;
    }

    @Override
    public List<CardProjectDO> getCardProjectList(CardProjectExportReqVO exportReqVO) {
        return baseMapper.selectList(exportReqVO);
    }

    @Override
    public CardProjectStatisticRespVO getStatistic(BigInteger projectId){
        CardProjectDO cardProjectDO = baseMapper.selectById(projectId);
        CardProjectStatisticRespVO respVO = new CardProjectStatisticRespVO();
        respVO.setId(projectId);
        respVO.setTotalNum(cardProjectDO.getTotalNum());
        respVO.setActivateNum(cardProjectDO.getActivateNum());
        List<CardDO> cardDOS = cardMapper.selectListByProjectId(projectId);
        BigDecimal faceValueTotal = cardDOS.stream().map(CardDO::getFaceValue).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        BigDecimal balanceTotal = cardDOS.stream().map(CardDO::getBalance).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        respVO.setAmountSpent(faceValueTotal.subtract(balanceTotal));
        respVO.setAmountSpentProportion(respVO.getAmountSpent().divide(faceValueTotal,2,RoundingMode.HALF_UP));
        return respVO;
    }


}
